/*
 * User.java
 */
package com.phonemap.models;

import java.util.Date;
import java.util.List;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.search.GeoPoint;
import com.phonemap.common.AppConstant;
import com.phonemap.common.Util;
import com.phonemap.dto.UserDTO;
import com.phonemap.helper.UserHelper;
import com.phonemap.search.SearchIndexManager;

/**
 * The Class User.
 */
public final class User {

	/** The user id field. */
	public static final String USER_ID_FIELD = "user_id";

	/** The fullname field. */
	public static final String FULLNAME_FIELD = "fullname";

	/** The link field. */
	public static final String LINK_FIELD = "link";

	/** The birthday field. */
	public static final String BIRTHDAY_FIELD = "birthday";

	/** The interested in field. */
	public static final String INTERESTED_IN_FIELD = "interested_in";

	/** The relationship status field. */
	public static final String RELATIONSHIP_STATUS_FIELD = "relationship_status";

	/** The gender field. */
	public static final String GENDER_FIELD = "gender";

	/** The email field. */
	public static final String EMAIL_FIELD = "email";

	/** The timezone field. */
	public static final String TIMEZONE_FIELD = "timezone";

	/** The locale field. */
	public static final String LOCALE_FIELD = "locale";

	/** The verified field. */
	public static final String VERIFIED_FIELD = "verified";

	/** The updated time field. */
	public static final String UPDATED_TIME_FIELD = "updated_time";

	/** The user name field. */
	public static final String USERNAME_FIELD = "username";

	/** The password field. */
	public static final String PASSWORD_FIELD = "password";

	/** The located field. */
	public static final String LOCATED_FIELD = "located";

	/** The located time field. */
	public static final String LOCATED_TIME_FIELD = "located_time";

	/** The phone field. */
	public static final String PHONE_FIELD = "phone";

	/** The user status field. */
	public static final String USER_STATUS_FIELD = "user_status";

	/**
	 * Instantiates a new user.
	 */
	private User() {

	}

	/**
	 * Insert user.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @param link
	 *            the link
	 * @param birthday
	 *            the birthday
	 * @param interestedIn
	 *            the interested in
	 * @param gender
	 *            the gender
	 * @param email
	 *            the email
	 * @param relationshipStatus
	 *            the relationship status
	 * @param timezone
	 *            the timezone
	 * @param locale
	 *            the locale
	 * @param verified
	 *            the verified
	 * @param updatedTime
	 *            the updated time
	 * @param username
	 *            the username
	 * @param password
	 *            the password
	 * @param located
	 *            the located
	 * @param locatedTime
	 *            the located time
	 * @param phone
	 *            the phone
	 * @return the entity
	 */
	public static Entity insertUser(final long id, final String fullname,
			final String link, final Date birthday, final String interestedIn,
			final String gender, final String email,
			final String relationshipStatus, final String timezone,
			final String locale, final String verified,
			final String updatedTime, final String username,
			final String password, final GeoPt located, final Date locatedTime,
			final String phone) {
		Entity user = getUser(id);
		if (user == null) {
			user = new Entity(AppConstant.ENTITY_USER, id);
			user.setProperty(USER_ID_FIELD, id);
			user.setProperty(FULLNAME_FIELD, fullname);
			user.setProperty(LINK_FIELD, link);
			user.setProperty(BIRTHDAY_FIELD, birthday);
			user.setProperty(INTERESTED_IN_FIELD, interestedIn);
			user.setProperty(RELATIONSHIP_STATUS_FIELD, relationshipStatus);
			user.setProperty(GENDER_FIELD, gender);
			user.setProperty(EMAIL_FIELD, email);
			user.setProperty(TIMEZONE_FIELD, timezone);
			user.setProperty(LOCALE_FIELD, locale);
			user.setProperty(VERIFIED_FIELD, verified);
			user.setProperty(UPDATED_TIME_FIELD, updatedTime);
			user.setProperty(USERNAME_FIELD, username);
			user.setProperty(PASSWORD_FIELD, password);
			user.setProperty(LOCATED_FIELD, located);
			user.setProperty(LOCATED_TIME_FIELD, locatedTime);
			user.setProperty(PHONE_FIELD, phone);
			user.setProperty(USER_STATUS_FIELD, AppConstant.STATUS_ON);
			Util.persistEntity(user);

			// input index
			// FIXME - need to debug getId() and getName()
			// FIXME - check condition when going on production
			if (located != null) {
				buildIndex(id, fullname, gender, located);

				// insert into position history
				Position.insertPosition(user, located, locatedTime);
			}
			
		} 

		return user;
	}
	
	/**
	 * Insert user without pos.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @param link
	 *            the link
	 * @param birthday
	 *            the birthday
	 * @param interested_in
	 *            the interested_in
	 * @param gender
	 *            the gender
	 * @param email
	 *            the email
	 * @param relationship_status
	 *            the relationship_status
	 * @param timezone
	 *            the timezone
	 * @param locale
	 *            the locale
	 * @param verified
	 *            the verified
	 * @param updated_time
	 *            the updated_time
	 * @param username
	 *            the username
	 * @param password
	 *            the password
	 * @param phone
	 *            the phone
	 * @return the entity
	 */
	public static Entity insertUserWithoutPos(final long id,
			final String fullname, final String link, Date birthday,
			final String interested_in, final String gender,
			final String email, final String relationship_status,
			final String timezone, final String locale, final String verified,
			final String updated_time, final String username,
			final String password, final String phone) {

		return insertUser(id, fullname, link, birthday, interested_in, gender,
				email, relationship_status, timezone, locale, verified,
				updated_time, username, password, null, null, phone);

	}

	/**
	 * Insert user.
	 * 
	 * @param dto
	 *            the dto
	 * @return the entity
	 */
	public static Entity insertUser(final UserDTO dto) {
		return insertUser(dto.getUserId(), dto.getFullname(), dto.getLink(),
				dto.getBirthday(), dto.getInterestedIn(), dto.getGender(),
				dto.getEmail(), dto.getRelationshipStatus(), dto.getTimezone(),
				dto.getLocale(), dto.getVerified(), dto.getUpdatedTime(),
				dto.getUsername(), dto.getPassword(), dto.getLocated(),
				dto.getLocatedTime(), dto.getPhone());
	}

	/**
	 * Insert user.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @return the entity
	 */
	public static Entity insertUser(final long id, final String fullname) {
		Entity user = getUser(id);
		if (user == null) {
			user = new Entity(AppConstant.ENTITY_USER, id);
			user.setProperty(USER_ID_FIELD, id);
			user.setProperty(FULLNAME_FIELD, fullname);
			user.setProperty(LINK_FIELD, null);
			user.setProperty(BIRTHDAY_FIELD, null);
			user.setProperty(INTERESTED_IN_FIELD, null);
			user.setProperty(RELATIONSHIP_STATUS_FIELD, null);
			user.setProperty(GENDER_FIELD, null);
			user.setProperty(EMAIL_FIELD, null);
			user.setProperty(TIMEZONE_FIELD, null);
			user.setProperty(LOCALE_FIELD, null);
			user.setProperty(VERIFIED_FIELD, null);
			user.setProperty(UPDATED_TIME_FIELD, null);
			user.setProperty(USERNAME_FIELD, null);
			user.setProperty(PASSWORD_FIELD, null);
			user.setProperty(LOCATED_FIELD, null);
			user.setProperty(LOCATED_TIME_FIELD, null);
			user.setProperty(PHONE_FIELD, null);
			user.setProperty(USER_STATUS_FIELD, AppConstant.STATUS_ON);
			Util.persistEntity(user);

		}

		return user;
	}

	/**
	 * Update position.
	 * 
	 * @param id
	 *            the id
	 * @param located
	 *            the located
	 * @param locatedTime
	 *            the located time
	 * @return the entity
	 */
	public static Entity updatePosition(final long id, final GeoPt located,
			final Date locatedTime) {
		Entity user = getUser(id);
		if (user != null) {
			user.setProperty(LOCATED_FIELD, located);
			user.setProperty(LOCATED_TIME_FIELD, locatedTime);
			Util.persistEntity(user);

			String gender = null;
			if (user.getProperty(GENDER_FIELD) != null) {
				gender = user.getProperty(GENDER_FIELD).toString();
			}

			// input index
			String fullname = user.getProperty("fullname").toString();
			buildIndex(id, fullname, gender, located);

			Position.insertPosition(user, located, locatedTime);
			return user;
		} else {
			return null;
		}

	}

	/**
	 * Update user.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @param link
	 *            the link
	 * @param birthday
	 *            the birthday
	 * @param interested_in
	 *            the interested_in
	 * @param gender
	 *            the gender
	 * @param email
	 *            the email
	 * @param relationship_status
	 *            the relationship_status
	 * @param timezone
	 *            the timezone
	 * @param locale
	 *            the locale
	 * @param verified
	 *            the verified
	 * @param updated_time
	 *            the updated_time
	 * @param username
	 *            the username
	 * @param located
	 *            the located
	 * @param located_time
	 *            the located_time
	 * @param phone
	 *            the phone
	 * @return the entity
	 */
	public static Entity updateUser(final long id, final String fullname,
			final String link, final Date birthday, final String interested_in,
			final String gender, final String email,
			final String relationship_status, final String timezone,
			final String locale, final String verified,
			final String updated_time, final String username,
			final GeoPt located, final Date located_time, final String phone) {
		Entity user = getUser(id);
		if (user != null) {
			user.setProperty(FULLNAME_FIELD, fullname);
			user.setProperty(LINK_FIELD, link);
			user.setProperty(BIRTHDAY_FIELD, birthday);
			user.setProperty(INTERESTED_IN_FIELD, interested_in);
			user.setProperty(RELATIONSHIP_STATUS_FIELD, relationship_status);
			user.setProperty(GENDER_FIELD, gender);
			user.setProperty(EMAIL_FIELD, email);
			user.setProperty(TIMEZONE_FIELD, timezone);
			user.setProperty(LOCALE_FIELD, locale);
			user.setProperty(VERIFIED_FIELD, verified);
			user.setProperty(UPDATED_TIME_FIELD, updated_time);
			user.setProperty(USERNAME_FIELD, username);
			user.setProperty(LOCATED_FIELD, located);
			user.setProperty(LOCATED_TIME_FIELD, located_time);
			user.setProperty(PHONE_FIELD, phone);
			Util.persistEntity(user);

			// input index
			buildIndex(id, fullname, gender, located);

			// insert into position
			Position.insertPosition(user, located, located_time);

		} else {
			return null;
		}

		return user;
	}

	/**
	 * Gets the single user.
	 * 
	 * @return the single user
	 */
	// public static Entity getSingleUser(final long userId) {
	// Iterable<Entity> results = Util.listEntities(AppConstant.ENTITY_USER,
	// USER_ID_FIELD, userId);
	// List<Entity> entity = new ArrayList<Entity>();
	// for (Entity e : results) {
	// if (e != null) {
	// entity.add(e);
	// }
	// }
	// if (!entity.isEmpty()) {
	// return (Entity) entity.remove(0);
	// }
	// return null;
	// }

	/**
	 * Gets the all users.
	 * 
	 * @return the all users
	 */
	public static Iterable<Entity> getAllUsers() {
		Iterable<Entity> entities = Util.listEntities(AppConstant.ENTITY_USER,
				null, null);
		return entities;
	}

	/**
	 * Gets the all users.
	 * 
	 * @param kind
	 *            the kind
	 * @return the all users
	 */
	public static Iterable<Entity> getAllUsers(final String kind) {
		Iterable<Entity> entities = Util.listEntities(kind, null, null);
		return entities;
	}

	/**
	 * Gets the users.
	 * 
	 * @param fullname
	 *            the fullname
	 * @return the users
	 */
	public static Iterable<Entity> getUsers(final String fullname) {
		Iterable<Entity> entities = Util.listEntities(AppConstant.ENTITY_USER,
				FULLNAME_FIELD, fullname);
		return entities;
	}
	
	/**
	 * Gets the users without position.
	 *
	 * @return the users without position
	 */
	public static Iterable<Entity> getUsersWithoutPosition() {
		Iterable<Entity> entities = Util.listEntities(AppConstant.ENTITY_USER,
				LOCATED_TIME_FIELD);
		return entities;
	}

	/**
	 * Delete product entity.
	 * 
	 * @param productKey
	 *            : product to be deleted
	 * @return status string
	 */
	public static String deleteUser(final long productKey) {
		Key key = KeyFactory.createKey(AppConstant.ENTITY_USER, productKey);

//		Iterable<Entity> items = Position.getPositions(productKey);
//		if (items.hashCode() {
//			return "Cannot delete, as there are items associated with this user.";
//		}
		Util.deleteEntity(key);
		return AppConstant.SUCCESS_STATUS;
	}

	/**
	 * Gets the positions.
	 * 
	 * @param name
	 *            the name
	 * @return the positions
	 */
	public static List<Entity> getPositions(final String name) {
		Query query = new Query();
		Key parentKey = KeyFactory.createKey(AppConstant.ENTITY_USER, name);
		query.setAncestor(parentKey);
		query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
				Query.FilterOperator.GREATER_THAN, parentKey));
		List<Entity> results = Util.getDatastoreServiceInstance()
				.prepare(query).asList(FetchOptions.Builder.withDefaults());
		return results;
	}

	/**
	 * Gets the users.
	 * 
	 * @param searchBy
	 *            the search by
	 * @param searchFor
	 *            the search for
	 * @return the users
	 */
	public static Iterable<Entity> getUsers(final String searchBy,
			final String searchFor) {
		return Util.listEntities(AppConstant.ENTITY_USER, searchBy, searchFor);
	}

	/**
	 * Gets the user.
	 * 
	 * @param id
	 *            the id
	 * @return the user
	 */
	public static Entity getUser(final long id) {
		Key key = KeyFactory.createKey(AppConstant.ENTITY_USER, id);
		return Util.findEntity(key);
	}

	/**
	 * Gets the users.
	 * 
	 * @param latitude
	 *            the latitude
	 * @param longitude
	 *            the longitude
	 * @param distanceInMeters
	 *            the distance in meters
	 * @param offset
	 *            the offset
	 * @return the users
	 */
	public static List<Entity> getUsers(final double latitude,
			final double longitude, final int distanceInMeters, final int offset) {
		return UserHelper.getUsers(new GeoPoint(latitude, longitude),
				distanceInMeters, offset);
	}

	
	/**
	 * Gets the users.
	 *
	 * @param position the position
	 * @param distanceInMeters the distance in meters
	 * @param offset the offset
	 * @return the users
	 */
	public static List<Entity> getUsers(final GeoPoint position,
			final int distanceInMeters, final int offset) {
		return UserHelper.getUsers(position, distanceInMeters, offset);
	}

	/**
	 * Gets the users.
	 * 
	 * @param userId
	 *            the user id
	 * @param fullname
	 *            the fullname
	 * @param offset
	 *            the offset
	 * @return the users
	 */
	public static List<Entity> getUsers(final String userId,
			final String fullname, final int offset) {
		return UserHelper.getUsers(userId, fullname, offset);
	}

	/**
	 * Gets the users.
	 * 
	 * @param fullname
	 *            the fullname
	 * @param offset
	 *            the offset
	 * @return the users
	 */
	public static List<Entity> getUsers(final String fullname, final int offset) {
		return UserHelper.getUsers(fullname, offset);
	}

	/**
	 * Gets the users.
	 * 
	 * @param fullname
	 *            the fullname
	 * @param operator
	 *            the operator
	 * @param offset
	 *            the offset
	 * @return the users
	 */
	public static List<Entity> getUsers(final String fullname,
			final FilterOperator operator, final int offset) {
		return Util.getEntities(AppConstant.ENTITY_USER, FULLNAME_FIELD,
				fullname, offset, operator);
	}

	/**
	 * Builds the index.
	 *
	 * @param userId the user id
	 * @param fullname the fullname
	 * @param gender the gender
	 * @param located the located
	 */
	public static void buildIndex(final long userId, final String fullname,
			final String gender, final GeoPt located) {
		UserHelper.deleteOldIndex(userId);
		SearchIndexManager.INSTANCE.indexDocument(UserHelper.getIndex(),
				UserHelper.buildDocument(userId, fullname,
						gender, located));
		
	}

}
